package java_option.audio_a013.audio;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

/**
 * 
 * @author java红了 | java_option <br>
 *         2020-11-419:22:44
 */
public class AudioPlay {

	private List<PlayItem> playList = new ArrayList<>();

	private PlayThread playThread;

	class PlayItem {
		String wavFile;
		long microseconds;
		boolean isInsert = false;//
		boolean isLoop = false; //
	}

	public AudioPlay(String... audios) {
		for (String audio : audios) {
			if (new File(audio).exists() == false) {
				continue;
			}
			PlayItem item = new PlayItem();
			item.wavFile = audio;
			item.microseconds = 0;
			playList.add(item);
		}
	}

	public static void main(String[] args) {

		File file = new File("d:/test/收藏/张靓颖-千百度.wav");
//

		new AudioPlay(file.getPath()).onStart(0, 0);
	}

	/**
	 * 
	 * @param file
	 * @param microseconds 微秒
	 */
	public void play(String filePath, long microseconds) {
		try {
			RandomAccessFile raf = new RandomAccessFile(filePath, "rw");

//
			System.out.println("--------RIFF--------");
			print(raf, 4, false);
			print(raf, 4, true);
			print(raf, 4, false);

//
			System.out.println("--------FORMAT--------");
			print(raf, 4, false);
			print(raf, 4, true);
			print(raf, 2, true);
			int channels = (int) print(raf, 2, true);

			int sampleRate = (int) print(raf, 4, true);
			print(raf, 4, true);
			print(raf, 2, true);
			int sampleSizeInBits = (int) print(raf, 2, true);

//
			System.out.println("--------DATA--------");
			print(raf, 4, false);
			print(raf, 4, true);

			boolean isBigEndian = true;
			AudioFormat format = new AudioFormat(sampleRate, sampleSizeInBits, channels, true, isBigEndian);

			final SourceDataLine auline;
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

			try {
				auline = (SourceDataLine) AudioSystem.getLine(info);
				auline.open(format);
			} catch (LineUnavailableException e) {
				e.printStackTrace();
				return;
			} catch (Exception e) {
				e.printStackTrace();
				return;
			}
			auline.start();
			try {
//
				int headSize = 12 + 24 + 8;
//
				final int bytePerSec = sampleRate * channels * sampleSizeInBits / 8;
				long skipData = microseconds * bytePerSec / (1000 * 1000);
				int blockAlign = channels * sampleSizeInBits / 8;
//
				if (skipData % blockAlign != 0) {
//
					skipData -= skipData % skipData;
				}
				raf.seek(headSize + skipData);
//
				int bytePerFrame = sampleSizeInBits * channels / 8;
				byte[] data = new byte[bytePerFrame];
//
				System.out.println("准备播放 " + filePath);
				System.out.println(microseconds);
//
				playThread = new PlayThread(filePath, raf, data, bytePerSec, isBigEndian, auline);
				playThread.start();
			} catch (IOException e) {
				e.printStackTrace();
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public class PlayThread extends Thread {
		private Boolean isRunning = false;

		private RandomAccessFile raf = null;
		private byte[] data;
		private String filePath;
		private int bytePerSec;
		private boolean isBigEndian;
		private SourceDataLine auline;

		public PlayThread(String filePath, RandomAccessFile raf, byte[] data, int bytePerSec, //
				boolean isBigEndian, SourceDataLine auline) {
			this.filePath = filePath;
			this.raf = raf;
			this.data = data;
			this.bytePerSec = bytePerSec;
			this.isBigEndian = isBigEndian;
			this.auline = auline;
		}

		@Override
		public void run() {
			try {
				isRunning = true;
				while (raf.read(data) > 0) {
//
					if (isRunning == null) {
						break;
					} else if (isRunning == false) {
						synchronized (playThread) {
							try {
								playThread.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						continue;//
					}
					if (isBigEndian) {
						byte[] little4 = revers(data);
						auline.write(little4, 0, little4.length);
					} else {
						auline.write(data, 0, data.length);
					}
				}
//
				auline.close();
//
				if (raf.getFilePointer() < raf.length()) {
//
					for (PlayItem item : playList) {
						if (item.wavFile.equals(filePath)) {
							item.microseconds = raf.getFilePointer() / bytePerSec * 1000 * 1000;
							break;
						}
					}
				} else {
					for (int i = 0; i < playList.size(); i++) {
						PlayItem item = playList.get(i);
						if (item.wavFile.equals(filePath)) {
							item.microseconds = 0;
							if (item.isLoop) {
								play(item.wavFile, item.microseconds);
								break;
							} else {
//
								if (playList.size() == 1) {
									break;
								} else {
//
									int nextIndex = 0;
									if (item.isInsert == true) {
										nextIndex = i - 1;
									} else {//
										nextIndex = i + 1;
									}
//
									if (nextIndex < 0 || nextIndex >= playList.size()) {
//
									} else {
										PlayItem next = playList.get(nextIndex);
										play(next.wavFile, next.microseconds);
									}
								}
							}
						}
					} //
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					isRunning = null;
					if (raf != null) {
						raf.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		public Boolean isRunning() {
			return isRunning;
		}

		public void setRunning(Boolean isRunning) {
			this.isRunning = isRunning;
		}
	}

	public void insert(String filePath, long microseconds) {
//
		for (Iterator<PlayItem> it = playList.iterator(); it.hasNext();) {
			PlayItem item = (PlayItem) it.next();
			if (item.wavFile.equals(filePath)) {
				it.remove();
			}
		}
		PlayItem item = new PlayItem();
		item.wavFile = filePath;
		item.microseconds = microseconds;
		if (playThread != null && playThread.isRunning() != null && playThread.isRunning() == true) {
			item.isInsert = true;
//
			playThread.setRunning(null);
		}
		this.playList.add(item);//
//
		play(filePath, microseconds);
	}

	public void onStart(int index, long microSeconds) {
		if (index >= playList.size()) {
			System.out.println("参数非法 ");
			System.out.println("当前播放列表的文件数量 " + playList.size());
			return;
		}
		if (playThread != null && playThread.isRunning() == true) {
//
			playThread.setRunning(null);
		}
		PlayItem item = playList.get(index);
		this.play(item.wavFile, microSeconds);
	}

	public void onPause() {
		if (playThread != null && playThread.isRunning() == true) {
//
			playThread.setRunning(false);
		}
	}

	public void onResume() {
		if (playThread != null && playThread.isRunning() == true) {
//
			playThread.setRunning(true);
		}
		playThread.notify();
	}

	public void onStop() {
		if (playThread != null && playThread.isRunning() == true) {
//
			playThread.setRunning(null);
		}
	}

	public static byte[] revers(byte[] tmp) {
		byte[] reversed = new byte[tmp.length];
		for (int i = 0; i < tmp.length; i++) {
			reversed[i] = tmp[tmp.length - i - 1];
		}
		return reversed;
	}

	private static Serializable print(RandomAccessFile raf, int byteSize, boolean isLittle) {
		byte[] data = new byte[byteSize];
		try {
			raf.read(data);
			if (isLittle) {
//
				if (byteSize == 4) {
					int value = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
					System.out.println(value);
					return value;
				} else {
					int value = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getShort();
					System.out.println(value);
					return value;
				}
			} else {
				String str = new String(data);
				System.out.println(str);
				return str;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}
